﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;

namespace CloudShot.Utils.Drawings
{
  public static class GraphicsExtensions
  {
    private static readonly Font _textUnderCursorFont = new Font(FontFamily.GenericSansSerif, 10);

    public static void DrawTextUnderCursor(this Graphics graphics, string text, Point location, int ownerWidth, int ownerHeight, bool onRightSide)
    {
      graphics.DrawTextUnderCursor(text, location, _textUnderCursorFont, ownerWidth, ownerHeight, onRightSide);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="graphics"></param>
    /// <param name="text"></param>
    /// <param name="location">A location of left top or right top corner (depends on <paramref name="onRightSide"/>) of the label in client coordinates.</param>
    /// <param name="ownerFont"></param>
    /// <param name="ownerWidth"></param>
    /// <param name="ownerHeight"></param>
    /// <param name="onRightSide"></param>
    public static void DrawTextUnderCursor(this Graphics graphics, string text, Point location, Font ownerFont, int ownerWidth, int ownerHeight,
                                           bool onRightSide)
    {
      SizeF labelSize = TextRenderer.MeasureText(text, ownerFont);

      int x;

      if (onRightSide)
        x = (location.X + labelSize.Width < ownerWidth) ? location.X : location.X - (int)labelSize.Width;
      else
        x = Math.Max(location.X - (int)labelSize.Width, 0);

      int y = (location.Y + labelSize.Height < ownerHeight) ? location.Y : location.Y - (int)labelSize.Height;

      if ((location.Y > y) && (location.X + labelSize.Width < ownerWidth))
        x = location.X;

      var labelRect = new Rectangle(x, y, (int)labelSize.Width, (int)labelSize.Height);
      graphics.FillRectangle(Brushes.WhiteSmoke, labelRect);

      TextRenderer.DrawText(graphics, text, ownerFont, labelRect, Color.Black,
                            TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.LeftAndRightPadding);
    }

    public static void DrawLabelOverImage(this Image image, string text, Font font)
    {
      if (string.IsNullOrEmpty(text))
        return;

      using (var graphics = Graphics.FromImage(image))
      {
        graphics.DrawPictureLabel(new Rectangle(0, 0, image.Width, image.Height), text, font);
      }
    }

    public static void DrawPictureLabel(this Graphics graphics, Rectangle rect, string text, Font font)
    {
      //calculating
      Size size = TextRenderer.MeasureText(text, font, rect.Size, TextFormatFlags.WordBreak);
      Rectangle textRect = new Rectangle(rect.X, rect.Bottom - size.Height, rect.Width, size.Height);

      //drawing
      graphics.FillRectangle(new SolidBrush(Color.FromArgb(200, Color.Black)), textRect);
      TextRenderer.DrawText(graphics, text, font, textRect, Color.WhiteSmoke, TextFormatFlags.WordBreak | TextFormatFlags.HorizontalCenter);
    }

    public static void DrawHint(this Graphics graphics, string text, Rectangle currentScreenClientBounds, Rectangle selectionRect, Font font)
    {
      //calculating
      //Size size = TextRenderer.MeasureText(text, font, new Size(500, currentScreenClientBounds.Height), TextFormatFlags.WordBreak);
      Size size = TextRenderer.MeasureText(text, font);

      int x = (currentScreenClientBounds.Width - size.Width) / 2;
      int y = currentScreenClientBounds.Height - 100;
      var textRect = new Rectangle(x, y, size.Width, size.Height);
      textRect.Inflate(4, 4);

      if (selectionRect.IntersectsWith(textRect))
        return;

      graphics.FillRoundedRectangle(new SolidBrush(Color.FromArgb(150, Color.Black)), textRect, 2);
      graphics.DrawRoundedRectangle(Pens.Black, textRect, 2);

      TextRenderer.DrawText(graphics, text, font, textRect, Color.White, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
    }

    /// <summary>
    /// Draws cross with center in <paramref name="position"/>.
    /// </summary>
    /// <param name="graphics"></param>
    /// <param name="position">A position of the cross center in client coordinates.</param>
    /// <param name="ownerWidth"></param>
    /// <param name="ownerHeight"></param>
    public static void DrawCross(this Graphics graphics, Point position, int ownerWidth, int ownerHeight)
    {
      using (var pen = new Pen(Color.FromArgb(180, Color.Gray)))
      {
        graphics.DrawLine(pen, position.X, 0, position.X, ownerHeight);
        graphics.DrawLine(pen, 0, position.Y, ownerWidth, position.Y);
      }
    }

    public static void DrawOutlineText(this Graphics graphics, string text, Font font, int width, int height, Color textColor, Color outlineColor,
                                       int outlineWidth)
    {
      // Gradient brush example
      // this makes the gradient repeat for each text line
      // var fr = new Rectangle(0, lblCapturer.Height - font.Height, lblCapturer.Width, font.Height);
      // var brush = new LinearGradientBrush(fr, ColorTranslator.FromHtml("#FF6493"), ColorTranslator.FromHtml("#D00F14"), 90);

      using (var brush = new SolidBrush(textColor))
      {
        graphics.DrawOutlineText(text, font, width, height, brush, outlineColor, outlineWidth);
      }
    }

    public static void DrawOutlineText(this Graphics graphics, string text, Font font, int width, int height, Brush textBrush, Color outlineColor,
                                       int outlineWidth)
    {
      GraphicsState state = graphics.Save();
      //float fontSize = 52;

      //this will center align our text at the bottom of the image
      var sf = new StringFormat
      {
        Alignment = StringAlignment.Center,
        LineAlignment = StringAlignment.Center
      };

      var p = new Pen(outlineColor, outlineWidth);
      p.LineJoin = LineJoin.Round; //prevent "spikes" at the path

      //this will be the rectangle used to draw and auto-wrap the text.
      //basically = image size
      var r = new Rectangle(0, 0, width, height);
      var gp = new GraphicsPath();

      //look mom! no pre-wrapping!
      gp.AddString(text, font.FontFamily, (int)font.Style, font.Size, r, sf);
      
      //these affect lines such as those in paths. Textrenderhint doesn't affect
      //text in a path as it is converted to ..well, a path.    
      graphics.SmoothingMode = SmoothingMode.AntiAlias;
      graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

      //TODO: shadow -> graphics.translate, fillpath once, remove translate
      graphics.DrawPath(p, gp);
      graphics.FillPath(textBrush, gp);

      //cleanup
      gp.Dispose();
      p.Dispose();
      sf.Dispose();
      graphics.Restore(state);
    }

    public static Font AppropriateFont(this Graphics g, float minFontSize, float maxFontSize, Size layoutSize, string s, Font f, out SizeF extent)
    {
      if (maxFontSize == minFontSize)
        f = new Font(f.FontFamily, minFontSize, f.Style);

      extent = g.MeasureString(s, f);

      if (maxFontSize <= minFontSize)
        return f;

      float hRatio = layoutSize.Height / extent.Height;
      float wRatio = layoutSize.Width / extent.Width;
      float ratio = (hRatio < wRatio) ? hRatio : wRatio;

      float newSize = f.Size * ratio;

      if (newSize < minFontSize)
        newSize = minFontSize;
      else if (newSize > maxFontSize)
        newSize = maxFontSize;

      f = new Font(f.FontFamily, newSize, f.Style);
      extent = g.MeasureString(s, f);

      return f;
    }
  }
}